package com.to8to.kitt;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.thrift.TBase;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocolFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.to8to.kitt.esb.ServiceMethodObj;
import com.to8to.kitt.utils.LogUtils;

public class ThriftClassFactory {

	public static Logger logger = LoggerFactory.getLogger(ThriftClassFactory.class);  
	
	public static TProtocolFactory ProtocolFactory = new TBinaryProtocol.Factory();
	
	public static Map<String, ServiceMethodObj> ServiceMethodMap = new HashMap<String, ServiceMethodObj>();
	
	public static ClassLoader classLoader = null;

	public static Class<?> loadThriftClass(String className)
	{
		Class<?> cls = null;
		try {
			cls = classLoader != null? classLoader.loadClass(className) : Class.forName(className);
			
			if(cls == null && classLoader != null)
				cls = Class.forName(className);
		} catch (ClassNotFoundException e) {
			logger.error("ClassNotFoundException error:{}\n{}", e, LogUtils.logStackTrace(e));
		}
		return cls;
	}
	
	public static ServiceMethodObj getServiceMethodObj(String service)
	{
		ServiceMethodObj obj = ServiceMethodMap.get(service);
		
		if(obj != null)
			return obj;
		
		Class<?> cls = loadThriftClass(service);
		if(cls != null)
			return initServiceMethodObj(cls);
		
		return null;
	}
	
	public static ServiceMethodObj initServiceMethodObj(Class<?> cls)
	{
		
		String serviceClassName = cls.getName();
		
		logger.info("load service: " + serviceClassName);
		
		ServiceMethodObj sm = new ServiceMethodObj();
		
		sm.serviceClass = cls;
		
		ServiceMethodMap.put(serviceClassName, sm);
		
		String IfaceClassName = serviceClassName + "$Iface";
		
		try {
		
			Class<?> IfaceClass = loadThriftClass(IfaceClassName);
			if(IfaceClass != null)
			{
				for (Method method : IfaceClass.getMethods()) 
				{
					String service = method.getName();
					String argsName = service + "_args";
					String resultName = service + "_result";
					Class<?> argsCls = loadThriftClass(serviceClassName + "$" + argsName);
					Class<?> resultCls = loadThriftClass(serviceClassName + "$" + resultName);
					TBase<?,?> args = (TBase<?,?>) argsCls.newInstance();
					TBase<?,?> result = (TBase<?,?>) resultCls.newInstance();
					sm.argsMap.put(service, args);
					sm.resultMap.put(service, result);
				}
			}
		} catch (InstantiationException e) {
			logger.error("InstantiationException:{}\n{}", e, LogUtils.logStackTrace(e));
		} catch (IllegalAccessException e) {
			logger.error("IllegalAccessException:{}\n{}", e, LogUtils.logStackTrace(e));
		}
		
		return sm;
	}
	
	public static void init(Class<?>... serviceClasses)
	{
		init(new TBinaryProtocol.Factory(), null, serviceClasses);
	}
	
	public static void init(TProtocolFactory protocolFactory, Class<?>... serviceClasses)
	{
		init(protocolFactory, null, serviceClasses);
	}
	
	
	public static void init(List<String> serviceClasseNames)
	{
		init(new TBinaryProtocol.Factory(), serviceClasseNames);
	}
	
	public static void init(TProtocolFactory protocolFactory, List<String> serviceClasseNames)
	{
		init(protocolFactory, null, serviceClasseNames);
	}
	
	public static void init(TProtocolFactory protocolFactory, ClassLoader classLoader, List<String> serviceClasseNames)
	{
		Class<?>[] classes = new Class<?>[serviceClasseNames.size()];
		
		for(int i = 0 ; i < classes.length; ++ i )
			classes[i] = loadThriftClass(serviceClasseNames.get(i));
		
		init(protocolFactory, classLoader, classes);
	}
	
	
	public static void init(TProtocolFactory protocolFactory, ClassLoader classLoader, Class<?>... serviceClasses)
	{
		if(protocolFactory != null)
			ThriftClassFactory.ProtocolFactory = protocolFactory;
		
		ThriftClassFactory.classLoader = classLoader;
		
		ServiceMethodMap.clear();
		
		for(Class<?> cls : serviceClasses)
			initServiceMethodObj(cls);
	}
}
